// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#include <cxxopts.hpp>

#include "coin/proto/coin_order_gateway.pb.h"
#include "coin2/app/driver.h"
#include "coin2/base/config/app_config.h"
#include "coin2/base/config/args.h"
#include "coin2/base/log.h"
#include "coin2/base/time.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/strategy/strategy.h"

using namespace coin2::exchange::base::symbology;
using namespace coin2::base::config;
using namespace coin2::exchange::feed;
using namespace coin2::app;
using coin2::exchange::base::feed::FeedUpdate;
using coin2::exchange::base::order::OrderUpdate;

struct Record {
  std::string market;
  std::string exchange;
  int64_t timestamp;
  std::string type;
  std::string product;
  double ask_price;
  double ask_qty;
  double bid_price;
  double bid_qty;

  Record() {
    ask_price = 0;
    ask_qty = 0;
    bid_price = 0;
    bid_qty = 0;
  }

  const char* ToCsv() {
    static char buffer[1024];
    ::snprintf(
        buffer,
        sizeof(buffer),
        "%s,%s,%ld,%s,%s,%f,%f,%f,%f\n",
        market.data(),
        exchange.data(),
        timestamp,
        type.data(),
        product.data(),
        ask_price,
        ask_qty,
        bid_price,
        bid_qty);
    return buffer;
  }

  static const char* header() {
    static char buffer[1024];
    ::snprintf(
        buffer,
        sizeof(buffer),
        "%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
        "market",
        "exchange",
        "timestamp",
        "type",
        "product",
        "ask_price",
        "ask_qty",
        "bid_price",
        "bid_qty");
    return buffer;
  }
};

class DumperStrategy : public ::coin2::strategy::IStrategy {
  void onTradeFeed(const FeedUpdate& upd) override {
    auto& trade = upd.trade();

    Record record;
    record.market = MarketType_Name(upd.market());
    record.exchange = ExchangeType_Name(upd.exchange());
    record.timestamp = upd.timestamp();
    record.product = upd.product().relative_norm();
    record.type = "trade";

    if (trade.side == TradeSide::TRADE_BUY_SIDE) {
      record.bid_price = trade.price;
      record.bid_qty = trade.fill_qty;
    } else {
      record.ask_price = trade.price;
      record.ask_qty = trade.fill_qty;
    }
    std::cout << record.ToCsv();
  }

  void onBookFeed(const FeedUpdate& upd) override {
    auto& book = upd.book();

    Record record;
    record.market = MarketType_Name(upd.market());
    record.exchange = ExchangeType_Name(upd.exchange());
    record.timestamp = upd.timestamp();
    record.product = upd.product().relative_norm();
    record.type = "book";

    auto ask = book.Ask0();
    if (ask) {
      record.ask_price = ask->price;
      record.ask_qty = ask->qty;
    }

    auto bid = book.Bid0();
    if (bid) {
      record.bid_price = bid->price;
      record.bid_qty = bid->qty;
    }
    std::cout << record.ToCsv();
  }

  void onLiquidationFeed(const FeedUpdate&) override {}
  void onAccountInfo(const OrderUpdate&) override {}
  void onAccountOrder(const OrderUpdate&) override {}
  void onOrderLog(const coin::proto::OrderGatewayLog&) override {}
};

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);

  DriverConfig app;

  cxxopts::Options opt("Printer", "Print feed and order events.");
  AddDriverOptions(&opt);
  AddIntervalOptions(&opt);
  AddProductOptions(&opt);

  auto res = opt.parse(argc, argv);
  ParseDriverOptions(res, &app);
  ParseIntervalOptions(
      res,
      app.mutable_feed()->mutable_common()->mutable_archive()->mutable_interval());
  ParseProductOptions(res, app.mutable_feed());

  LOG(INFO) << app.DebugString();

  DumperStrategy printer;  // printer unused if feed-writer
  Driver driver(app, &printer);
  std::cout << Record::header();
  driver.Run();

  return 0;
}
